package com.ease.framework {

import com.ease.util.ObjectFilter;

/**
 * @private
 *
 * Defines an object filter for the purposes of property injection.
 */
public class PropertyInjectionObjectSpec extends ObjectFilter {
   /**
    * The property whose value is to be injected into the injection target (i.e., it's a
    * property on the injection source object).
    */
   public var sourceProperty:String;

   /**
    * If the objects to be injected are <i>properties</i> of the spec-targeted objects (as
    * opposed to the targeted objects themselves being the injection objects), then this
    * type indicates the type of that property whose value is to be injected at the target
    * site.
    */
   public var specTargetedValueType:Class;

   /**
    * The array element type specified on the target property (i.e., the property whose
    * value is to be set through injection).
    */
   public var targetExpectedArrayElementType:Class;

   /**
    * Indicates whether the [Injectable] property in question should be a candidate for
    * re-injection every time there are changes in the dependent properties of the target.
    */
   public var isPropertyToBeReinjectedOnTargetChanges:Boolean = false;

   /**
    * Name of the method (on the injection target) that is suppposed to be called every
    * time the Injectable property of interest is recomputed for injection, even if it's
    * not actually injected due to the property having the same value as the newly
    * computed value.
    */
   public var postInjectionFinalizeCallbackName:String;

   /**
    * Name of the method (on the injection target) that is suppposed to be called every
    * time the Injectable property of interest is recomputed for injection but is skipped
    * (i.e., not actually injected) due to the property having the same value as the newly
    * computed value, or due to no matching objects being found.
    */
   public var postInjectionSkipCallbackName:String;

   /**
    * A convenience method to check whether the given object passes the full filter given
    * by this spec, including the property filter as well as the meta-property filter,
    * where the latter is interpreted by the given directories.
    *
    * @see #filterUsingDirectories()
    */
   public final function passesFilterUsingDirectories(
         object:Object, directories:Array/*IObjectDirectory*/ ):Boolean {
      var filterFunctor:Function = function( dir:IObjectDirectory, index:int,
                                             array:Array ):Boolean {
         return passesMetaFilterOfDirectory( object, dir );
      }
      var passesDirectories:Boolean = directories.every( filterFunctor );
      if (! passesDirectories) {
         // does not pass some directory's meta-filter
         return false;
      }
      if (! passesFilter( object )) {
         return false;
      }
      return true;
   }

   /**
    * A convenience method to return a filtered version of the given array, which contains
    * only those objects that satisfy the property filter (defined in this spec) as well
    * as the meta-filter (where the latter is interpreted by the given directories).
    *
    * @see #passesFilterUsingDirectories()
    */
   public final function filterUsingDirectories(
         objects:Array/*?*/, directories:Array/*IObjectDirectory*/ ):Array/*?*/ {
      var filterFunctor:Function = function( item:*, index:int, array:Array ):Boolean {
         return passesFilterUsingDirectories( item, directories );
      }
      var filteredObjects:Array = objects.filter( filterFunctor );
      return filteredObjects;
   }

   private function passesMetaFilterOfDirectory( object:Object,
                                                 directory:IObjectDirectory ):Boolean {
      return directory.evaluateMetaFilter( this, object );
   }
}
}
